#include <sparrow/http_connection.h>
#include <sparrow/http_connection_manager.h>
#include <sparrow/reactor.h>
#include <sparrow/util.h>

HttpConnection::HttpConnection(Reactor *r, HttpConnectionManager *manager, VariantSet *connection_attributes)
    :   Handler(r),
        connection_attributes_(connection_attributes),
        state_(ACCEPTED),
        ignore_timeout_(true),
        parser_(&http_header_),
        manager_(manager),
        server_fd_(-1)
{
    server_fd_ = connection_attributes_->get("server-fd")->int32();
    register_once(this, &HttpConnection::once);
}

void HttpConnection::once(VariantSet *v)
{
    printf("HttpConnection::once\n");
    token_ = get_reactor()->create_token();
    get_reactor()->register_input_handler(server_fd_, this, token_);
    get_reactor()->schedule_timer(this, totimespec(60, 0), token_); 
    printf("HttpConnection::HttpConnection server_fd: %d\n", server_fd_);
    printf("HttpConnection::HttpConnection token: %lld\n", token_);
    printf("HttpConnection::HttpConnection this: %x\n", (uint32_t)this);
}

void HttpConnection::read_request_header(const Octets &input)
{
    printf("HttpConnection::read_request_header\n");
    ignore_timeout_ = true;
    Octets reminder;
    bool complete_header = parser_.parse_request(input, reminder);
    if(complete_header)
    {
        const std::string *content_length = http_header_.header("Content-Length", std::nothrow);
        const std::string *transfer_encoding = http_header_.header("Transfer-Encoding", std::nothrow);
        if(transfer_encoding && (*transfer_encoding).compare("identity") != 0)
        {
            // chunked request body
        }
        else if(content_length)
        {
            // request body
        }
        else
        {
            if(!reminder.empty())
            {
                // pipeline
            }
            else
            {
                // done request - it still can be pipeline
                // but we will know it only after next read
                change_state(SERVING_REQUEST);
                send_to_backend();
            }
        }
    }
    else
    {
        // read more
    }
}

void HttpConnection::send_to_backend()
{
    VariantSet *http_request = http_header_.release_header();
    printf("HttpConnection::send_to_backend\n");
    manager_->backend(this, http_request);
}

void HttpConnection::handle_input(int fd, uint64_t token)
{
    printf("HttpConnection::handle_input fd: %d, token: %lld\n", fd, token);
    Octets buffer(8 * 1024, 0);
    ssize_t result = recv(server_fd_, &buffer.at(0), buffer.size(), 0);
    printf("read result: %d\n", result);
    if(result > 0)
    {
        buffer.resize(result);
        switch(state_)
        {
            case ACCEPTED:
                change_state(READ_REQUEST_HEADER); 
            case READ_REQUEST_HEADER:
                read_request_header(buffer);
                break;

            case READ_REQUEST_BODY:
                break;

            case READ_PIPELINE_REQUESTS:
                break;

            case SERVING_REQUEST:
                break;

            default:
                break;
        }
    }
    else if(result == 0)
    {
        get_reactor()->unregister_input_handler(token_);
        get_reactor()->cancel_timer(token_);
        manager_->remove(this);
    }
    else
    {
        // error
    }
}

void HttpConnection::handle_output(int fd, uint64_t token)
{
    printf("HttpConnection::handle_output fd: %d, token: %lld\n", fd, token);
}

void HttpConnection::handle_error(int fd, uint64_t token)
{
    printf("HttpConnection::handle_error fd: %d, token: %lld\n", fd, token);
}

void HttpConnection::handle_timeout(uint64_t token)
{
    printf("HttpConnection::handle_timeout token: %lld\n", token);
}

void HttpConnection::change_state(State new_state)
{
    state_ = new_state;
}

int64_t HttpConnection::get_token() const
{
    return token_;
}

void HttpConnection::content_response(VariantSet *v)
{
}

void HttpConnection::error_response(VariantSet *v)
{
}

